﻿using Bit.Art.Core.Extensions;
using Bit.Art.Core.Models;
using Bit.Art.Db;
using Bit.Art.Distributed;
using Disruptor;
using Microsoft.Extensions.Logging;
using NetMQ;
using NetMQ.Sockets;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.EntityFrameworkCore;
using Bit.Art.Core;
using Serilog;

namespace Bit.Art.Engine
{
    public class OrderMatchHandler : IEventHandler<Order>
    {
#if DEBUG || UNITTEST
        public
#else
        private
#endif
         static MemoryOrderBookRepo[] s_MemOrderBookRepo;
        private Memcache _memcache;
        private PushSocket _bookeeper;
        private RouterSocket _cancelResponse;
        private long _orderno;

        public OrderMatchHandler(Memcache memcache, PushSocket bookeeper, RouterSocket canceller, int depth, int orderCapacity)
        {
            _memcache = memcache;
            _bookeeper = bookeeper;
            _cancelResponse = canceller;
            if (s_MemOrderBookRepo == null)
            {
                InitializeMemOrderRepo(depth, orderCapacity);
            }
        }

        private void InitializeMemOrderRepo(int depth, int orderCapacity)
        {
            using (var db = Program.GlobalServiceProvider.GetService<ExchangeConfigDb>())
            {
                Console.WriteLine($"链接到服务器：{db.Database.GetDbConnection().DataSource}");
                var configs = db.Markets.OrderBy(m => m.Name).Where(m => m.IsEnabled).ToArray();
                // 注意不要使用List来构建，为了节省内存，直接创建数组，每个交易对在内存里的缓存订单数由人工事先计算好配置
                // 数组是排序的，按Market字段从小到大排序
                var repoList = new List<MemoryOrderBookRepo>();
                foreach (var config in configs)
                {
                    repoList.Add(new MemoryOrderBookRepo(config.Name, config.DepthInMemory, config.OrderCapacityInMemory));
                }

                s_MemOrderBookRepo = repoList.ToArray();
            }
        }

        public byte UpdateMarket(Market market)
        {
            var repoList = new List<MemoryOrderBookRepo>(s_MemOrderBookRepo);
            var idx = Array.BinarySearch(s_MemOrderBookRepo, market.Name, s_RepoMarketComparer);
            if (market.IsEnabled && idx >= 0) return Constants.ErrorCodes.MARKET_EXISTS;
            if (!market.IsEnabled && idx < 0) return Constants.ErrorCodes.MARKET_NOT_EXISTS;

            var repo = new MemoryOrderBookRepo(market.Name, market.DepthInMemory, market.OrderCapacityInMemory);
            if (market.IsEnabled)
            {
                repoList.Add(repo);
            }
            else
            {
                repoList.RemoveAt(idx);
            }

            repoList.Sort(s_RepoMarketComparer);
            s_MemOrderBookRepo = repoList.ToArray();
            return 0;
        }

        private static MemoryOrderBookRepo.MarketComparer s_RepoMarketComparer = new MemoryOrderBookRepo.MarketComparer();
        public async void OnEvent(Order order, long sequence, bool endOfBatch)
        {
            var idx = Array.BinarySearch(s_MemOrderBookRepo, order.Market, s_RepoMarketComparer);
            if (idx < 0)
            {
#if UNITTEST
                Program.s_autoResetEventForTest.Set();
#endif
                return;
            }
            
            var repo = s_MemOrderBookRepo[idx];
            if (order.IsCancelled)
            {
                await TryCancelOrder(repo, _memcache, order, _bookeeper, _cancelResponse);
            }
            else
            {
                await PushNewOrder(repo, _memcache, order, _bookeeper);
            }

#if UNITTEST
            Program.s_autoResetEventForTest.Set();
#endif
        }

        private async Task<bool> TryCancelOrder(MemoryOrderBookRepo repo, Memcache memcache, Order order, PushSocket bookeeper, RouterSocket canceller)
        {
            var orderid = order.Id;
            var idx = await memcache.GetOrderIndex(orderid);
            var ret = repo.CancelOrder(idx.Side, idx.Price, idx.OrderId, ref order);

            // 一定要给response
            var replyMsg = new NetMQMessage();
            replyMsg.Append(order.Id.ToByteArray());
            replyMsg.AppendEmptyFrame();
            replyMsg.Append(ret ? new byte[] { 1 } : new byte[] { 0 });
            canceller.SendMultipartMessage(replyMsg);

            // order.IsCancelled 应该是 true
            var msg = new NetMQMessage();
            msg.Append(order.ToFixedBytes());
            bookeeper.SendMultipartMessage(msg);
            return true;
        }

        private async Task PushNewOrder(MemoryOrderBookRepo repo, Memcache memcache, Order order, PushSocket bookeeper)
        {
            var filled = repo.Push(order, out ulong orderid, out Order[] matched);
            Log.Logger.Debug($"[Engine] push order no: {++_orderno}, side: {order.Side}, price {order.Price}, volume {order.Volume}, type: {order.OrderType}");

            if (filled != OrderFillType.Filled && order.OrderType == (int)OrderType.Limit)
            {
                Log.Logger.Debug("[Engine] 订单未完全撮合！");
                // 1.3. 将用户的订单保存到内存缓存中，建立内存索引以便快速查询
                var mcidx = new MemCachedOrderIndex(order.Side, order.Price.Value, orderid);
                await memcache.InsertIndexForDbAndMemCache(order, mcidx);
                await memcache.UpdateUserOrderIndex(order, mcidx);
                PublishOrders(order, matched, bookeeper);
            }
            else if (filled == OrderFillType.Filled && order.OrderType == (int)OrderType.Limit)
            {
                Log.Logger.Debug("[Engine] 订单完全撮合！");
                PublishOrders(order, matched, bookeeper);
            }
            else
            {
                Log.Logger.Debug("[Engine] 订单未推送到下一个节点！");
            }
        }

        private void PublishOrders(Order order, Order[] matched, PushSocket bookeeper)
        {
            var msg = new NetMQMessage();
            var bytes = order.ToFixedBytes();
            msg.Append(bytes);
            // 2.1. 将最新的成交价广播
            // 2.2. 广播最新的市场深度
            // 2.3. 执行k线图需要的相关统计并广播
            // 2.4. 对于有成交的订单，广播最新的成交金额和数量
            var mem = BitConverter.ToString(bytes);
            Log.Logger.Debug($"[Engine] PublishOrders, msg len: {bytes.Length}, msg: {mem}！");
            bookeeper.SendMultipartMessage(msg);
            
            if (matched != null)
            {
                foreach (var mo in matched)
                {
                    msg = new NetMQMessage();
                    msg.Append(mo.ToFixedBytes());
                    _bookeeper.SendMultipartMessage(msg);
                }
            }
        }
    }
}
